package generate

import (
	"fmt"
	"github.com/golang/protobuf/protoc-gen-go/generator"
	"gorm_create_struct/conf"
	"gorm_create_struct/db"
	"io"
	"os"
	"strings"
	"sync"
	"unicode"
)


// @isCreateAll 是否生成全部表名
// @tableNames	数据库表名，若要生成全部表时，不用传
func Generate(isCreateAll bool, tableNames ...string) {
	tableNamesStr := ""
	if len(tableNames) == 0 && isCreateAll == false {
		fmt.Println("请输入表名")
	}
	tables := []Table{}
	for _, name := range tableNames {
		tables = append(tables, Table{
			Name:    name,
			Comment: "",
		})
	}

	//生成所有表信息
	if isCreateAll {
		tables = getTables(tableNamesStr)
	}

	wg := sync.WaitGroup{}
	wg.Add(len(tables)*2)
	for _, table := range tables {
		fields := getFields(table.Name)
		go generateModel(table, fields, &wg)
		go generateJSON(table, fields, &wg)
	}

	wg.Wait()

}

//获取表信息
func getTables(tableNames string) []Table {

	query := db.Instance.Debug()
	var tables []Table
	if tableNames == "" {
		query.Raw("SELECT TABLE_NAME as Name,TABLE_COMMENT as Comment FROM information_schema.TABLES WHERE table_schema='" + conf.MasterDbConfig.DbName + "';").Scan(&tables)
	}

	return tables
}

//获取所有字段信息
func getFields(tableName string) []Field {

	query := db.Instance.Debug()
	var fields []Field
	query.Raw("show FULL COLUMNS from " + tableName + ";").Find(&fields)
	return fields
}

//生成Model
func generateModel(table Table, fields []Field, wg *sync.WaitGroup) {
	content := "package models\n\n"
	//表注释
	if len(table.Comment) > 0 {
		content += "// " + table.Comment + "\n"
	}
	content += "type " + generator.CamelCase(table.Name) + " struct {\n"
	//生成字段
	for _, field := range fields {
		fieldName := generator.CamelCase(field.Field)
		//fieldJson := getFieldJson(field)
		fieldGorm := getFieldGorm(field)
		fieldType := getFiledType(field)
		fieldComment := getFieldComment(field)
		content += "	" + fieldName + " " + fieldType + " `" + fieldGorm + "` " + fieldComment + "\n"
	}
	content += "}\n"

	content += "func (entity *" + generator.CamelCase(table.Name) + ") TableName() string {\n"
	content += "	" + `return "` + table.Name + `"`
	content += "\n}"

	filename := conf.ModelPath + table.Name + ".go"
	var f *os.File
	var err error
	if checkFileIsExist(filename) {
		fmt.Println(table.Name + " 已存在，需删除才能重新生成...")
		f, err = os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666) //打开文件
		wg.Done()
		if err != nil {
			panic(err)
		}
		return
	}

	f, err = os.Create(filename)
	if err != nil {
		panic(err)
	}

	defer f.Close()
	_, err = io.WriteString(f, content)
	if err != nil {
		panic(err)
	} else {
		fmt.Println(generator.CamelCase(table.Name) + " 已生成...")
	}

	wg.Done()
}

//获取字段类型
func getFiledType(field Field) string {
	typeArr := strings.Split(field.Type, "(")
	typeArr1 := strings.Split(field.Type, ")")

	switch typeArr[0] {
	case "int":
		if typeArr1[1] == " unsigned" {
			return "*uint32"
		} else {
			return "*int32"
		}
	case "integer":
		if typeArr1[1] == " unsigned" {
			return "*uint32"
		} else {
			return "*int32"
		}
	case "mediumint":
		if typeArr1[1] == " unsigned" {
			return "*uint32"
		} else {
			return "*int32"
		}
	case "bit":
		if typeArr1[1] == " unsigned" {
			return "*uint32"
		} else {
			return "*int32"
		}
	case "year":
		if typeArr1[1] == " unsigned" {
			return "*uint32"
		} else {
			return "*int32"
		}
	case "smallint":
		if typeArr1[1] == " unsigned" {
			return "*uint16"
		} else {
			return "*int16"
		}
	case "tinyint":
		if typeArr1[1] == " unsigned" {
			return "*uint8"
		} else {
			return "*int8"
		}
	case "bigint":
		if typeArr1[1] == " unsigned" {
			return "*uint64"
		} else {
			return "*int64"
		}
	case "decimal":
		return "*float64"
	case "double":
		return "*float32"
	case "float":
		return "*float32"
	case "real":
		return "*float32"
	case "numeric":
		return "*float32"
	case "timestamp":
		return "*time.Time"
	case "datetime":
		return "*jsontime.JsonTime"
	case "time":
		return "*time.Time"
	case "date":
		return "*time.Time"
	default:
		return "*string"
	}
}

//获取字段json描述
func getFieldJson(field Field) string {
	return `json:"` + Lcfirst(generator.CamelCase(field.Field)) + `"`
}

// 首字母小写
func Lcfirst(str string) string {
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return ""
}

//获取字段gorm描述
func getFieldGorm(field Field) string {
	fieldContext := `gorm:"column:` + field.Field

	if field.Key == "PRI" {
		fieldContext = fieldContext + `;primaryKey`
	}
	if field.Key == "UNI" {
		fieldContext = fieldContext + `;unique`
	}
	if field.Extra == "auto_increment" {
		fieldContext = fieldContext + `;autoIncrement`
	}
	if field.Null == "NO" {
		fieldContext = fieldContext + `;not null`
	}
	return fieldContext + `"`
}

//获取字段说明
func getFieldComment(field Field) string {
	if len(field.Comment) > 0 {
		//return "// " + field.Comment
		return "//" + strings.Replace(strings.Replace(field.Comment, "\r", "\\r", -1), "\n", "\\n", -1)
	}
	return ""
}

//检查文件是否存在
func checkFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}

//生成JSON
func generateJSON(table Table, fields []Field, wg *sync.WaitGroup) {
	content := "package reply\n\n"

	content += "type " + generator.CamelCase(table.Name) + "Reply struct {\n"
	//生成字段
	for _, field := range fields {
		fieldName := generator.CamelCase(field.Field)
		fieldJson := getFieldJson(field)
		fieldType := getFiledType(field)
		content += "	" + fieldName + " " + fieldType + " `" + fieldJson + "` " + "\n"
	}
	content += "}\n"

	filename := conf.JsonPath + table.Name + "_reply.go"
	if checkFileIsExist(filename) {
		fmt.Println(generator.CamelCase(table.Name) + " 已存在，需删除才能重新生成...")
		wg.Done()
		return
	}
	f, err := os.Create(filename)
	if err != nil {
		wg.Done()
		panic(err)
	}

	_, err = io.WriteString(f, content)
	if err != nil {
		wg.Done()
		panic(err)
	} else {
		fmt.Println(table.Name + "_reply.go 已生成...")
	}
	defer f.Close()

	wg.Done()

}